import unittest
from gamestate import *


class GamestateTest(unittest.TestCase):
    def test_gamestate_init(self):
        NUM_FIELDS = 8
        val = range(NUM_FIELDS)

        gamestate = GameState(*val)
        self.assertEqual([gamestate.players, gamestate.hat,
                          gamestate.game_round, gamestate.phase,
                          gamestate.rounds,  gamestate.current_added_player,
                          gamestate.settings, gamestate.game_type], val)

    def test_gamestate_eq_and_neq(self):
        NUM_FIELDS = 8
        val = range(2)
        self.assertNotEqual(*val)

        gs1 = GameState(*([None] * NUM_FIELDS))
        gs2 = GameState(*([None] * NUM_FIELDS))
        self.assertTrue(gs1 == gs2)
        self.assertFalse(gs1 != gs2)

        for i in range(NUM_FIELDS):
            arg1 = [None] * i + [val[0]] + [None] * (NUM_FIELDS - i - 1)
            arg2 = [None] * i + [val[1]] + [None] * (NUM_FIELDS - i - 1)
            gs1 = GameState(*arg1)
            gs2 = GameState(*arg2)
            self.assertTrue(gs1 != gs2)
            self.assertFalse(gs1 == gs2)

        gs1 = GameState(*([None] * NUM_FIELDS))
        gs2 = GameState(*([None] * NUM_FIELDS))
        gs1.is_paused, gs2.is_paused = val
        self.assertTrue(gs1 != gs2)
        self.assertFalse(gs1 == gs2)

        gs1.is_paused, gs2.is_paused = None, None
        gs1.remote_id, gs2.remote_id = val
        self.assertTrue(gs1 != gs2)
        self.assertFalse(gs1 == gs2)

        gs1.remote_id, gs2.remote_id = None, None
        gs1.pin_code, gs2.pin_code = val
        self.assertTrue(gs1 != gs2)
        self.assertFalse(gs1 == gs2)

        gs1.pin_code, gs2.pin_code = None, None
        gs1.is_joining, gs2.is_joining = val[1], val[0]
        self.assertTrue(gs1 != gs2)
        self.assertFalse(gs1 == gs2)

    def test_gamestate_repr_and_str(self):
        NUM_FIELDS = 13
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        gs = GameState(*val[:8])
        gs.is_paused = val[8]
        gs.remote_id = val[9]
        gs.pin_code = val[10]
        gs.is_joining = val[11]
        gs.circle_num = val[12]

        strgs = str(gs)
        self.assertEqual(strgs, gs.__repr__())
        for part in strgs.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(GameState)')

    def test_gamestate_clear(self):
        NUM_FIELDS = 8
        val = range(NUM_FIELDS)

        gs1 = GameState(*val)
        gs2 = GameState(*([None] * NUM_FIELDS))
        gs1.clear()
        self.assertEqual(gs1, gs2)

    def test_phase(self):
        phase = Phase()

    def test_gameround_init(self):
        NUM_FIELDS = 5
        val = range(NUM_FIELDS)

        gr = GameRound(*val)
        self.assertEqual([gr.time_left_sec, gr.words, gr.current_word,
                          gr.players_pair, gr.is_replayed], val)

    def test_gameround_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        gr1 = GameRound(None, None, None, None, None)
        gr2 = GameRound(None, None, None, None, None)
        self.assertTrue(gr1 == gr2)
        self.assertFalse(gr1 != gr2)

        gr1 = GameRound(val[0], None, None, None, None)
        gr2 = GameRound(val[1], None, None, None, None)
        self.assertTrue(gr1 != gr2)
        self.assertFalse(gr1 == gr2)

        gr1 = GameRound(None, val[0], None, None, None)
        gr2 = GameRound(None, val[1], None, None, None)
        self.assertTrue(gr1 != gr2)
        self.assertFalse(gr1 == gr2)

        gr1 = GameRound(None, None, val[0], None, None)
        gr2 = GameRound(None, None, val[1], None, None)
        self.assertTrue(gr1 != gr2)
        self.assertFalse(gr1 == gr2)

        gr1 = GameRound(None, None, None, val[0], None)
        gr2 = GameRound(None, None, None, val[1], None)
        self.assertTrue(gr1 != gr2)
        self.assertFalse(gr1 == gr2)

        gr1 = GameRound(None, None, None, None, val[0])
        gr2 = GameRound(None, None, None, None, val[1])
        self.assertTrue(gr1 != gr2)
        self.assertFalse(gr1 == gr2)

    def test_gameround_repr_and_str(self):
        NUM_FIELDS = 5
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        gr = GameRound(*val)

        strgr = str(gr)
        self.assertEqual(strgr, gr.__repr__())
        for part in strgr.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(GameRound)')

    def test_nextpairstrategy_init(self):
        NUM_FIELDS = 1
        val = range(NUM_FIELDS)

        nps = NextPairStrategy(*val)
        self.assertEqual([nps.list_of_players,  nps.index_1,  nps.index_2,
                          nps.is_players_deleted, nps.is_same_pair],
                         [val[0], None, None, False, False])

    def test_nextpairstrategy_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        nps1 = NextPairStrategy(None)
        nps2 = NextPairStrategy(None)
        self.assertTrue(nps1 == nps2)
        self.assertFalse(nps1 != nps2)

        nps1 = NextPairStrategy(val[0])
        nps2 = NextPairStrategy(val[1])
        self.assertTrue(nps1 != nps2)
        self.assertFalse(nps1 == nps2)

        nps1 = NextPairStrategy(None)
        nps2 = NextPairStrategy(None)
        nps1.index_1, nps2.index_1 = val
        self.assertTrue(nps1 != nps2)
        self.assertFalse(nps1 == nps2)

        nps1.index_1, nps2.index_1 = None, None
        nps1.index_2, nps2.index_2 = val
        self.assertTrue(nps1 != nps2)
        self.assertFalse(nps1 == nps2)

        nps1.index_2, nps2.index_2 = None, None
        nps1.is_players_deleted, nps2.is_players_deleted = val
        self.assertTrue(nps1 != nps2)
        self.assertFalse(nps1 == nps2)

        nps1.index_2, nps2.index_2 = None, None
        nps1.is_same_pair, nps2.is_same_pair = val
        self.assertTrue(nps1 != nps2)
        self.assertFalse(nps1 == nps2)

    def test_nextpairstrategy_repr_and_str(self):
        NUM_FIELDS = 5
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        nps = NextPairStrategy(val[0])
        nps.list_of_players, nps.index_1, nps.index_2, \
            nps.is_players_deleted, nps.is_same_pair =\
            val

        strnps = str(nps)
        self.assertEqual(strnps, nps.__repr__())
        for part in strnps.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(NextPairStrategy)')

    def strategy_setUp(self):
        NUM_PLAYERS = 8
        NUM_ROUNDS = 100

        self.players_list = [i for i in range(NUM_PLAYERS)]
        self.donor = dict()
        self.recipient = dict()
        for player in self.players_list:
            self.donor[player] = 0
            self.recipient[player] = 0
        self.round_cnt = NUM_ROUNDS

    def strategy_test_run_rounds(self):
        for round_num in range(self.round_cnt):
            players = self.strategy.get_next_pair()
            self.donor[players[0]] += 1
            self.recipient[players[1]] += 1

            min_value = self.round_cnt
            max_value = 0
            for player in self.players_list:
                min_value = min(min_value, self.donor[player])
                max_value = max(max_value, self.donor[player])
                min_value = min(min_value, self.recipient[player])
                max_value = max(max_value, self.recipient[player])

                self.assertTrue(max_value - min_value <= 1)

            pref_players = players

    def test_strategy_is_pair_traditional(self):
        self.strategy_setUp()
        self.strategy = TraditionalStrategy(self.players_list)
        self.assertTrue(self.strategy.is_pair_strategy())

    def test_strategy_get_next_pair_traditional(self):
        """Test get_next_pair in TraditionalStrategy
        """
        self.strategy_setUp()
        self.strategy = TraditionalStrategy(self.players_list)
        self.strategy_test_run_rounds()

    def test_strategy_get_same_pair_traditional(self):
        """Test get_next_pair in TraditionalStrategy when is_same_pair == True
        """
        self.strategy_setUp()
        self.strategy = TraditionalStrategy(self.players_list)
        self.strategy.is_same_pair = True

        pair = self.strategy.get_next_pair()
        ok_pair = (self.players_list[0],
                   self.players_list[len(self.players_list)//2])
        self.assertEqual(pair, ok_pair)

        pair = self.strategy.get_next_pair()
        ok_pair = (self.players_list[1],
                   self.players_list[1+len(self.players_list)//2])
        self.assertEqual(pair, ok_pair)

        for round_num in range(self.round_cnt):
            ok_pair = self.strategy.get_next_pair()
            self.strategy.is_same_pair = True
            pair = self.strategy.get_next_pair()
            self.assertEqual(pair, ok_pair)

    def test_strategy_del_and_get_next_pair_traditional(self):
        self.strategy_setUp()
        self.strategy = TraditionalStrategy(self.players_list)

        first_pair = (self.players_list[0],
                      self.players_list[len(self.players_list)//2])
        self.strategy.is_players_deleted = True
        try:
            pair = self.strategy.get_next_pair()
        except:
            self.fail(msg='cannot get first pair when is_pairs_deleted')
        self.assertEqual(pair, first_pair)

        self.strategy.list_of_players.remove(first_pair[0])
        self.strategy.list_of_players.remove(first_pair[1])
        next_pair = (self.players_list[0],
                     self.players_list[len(self.players_list)//2])
        self.strategy.is_players_deleted = True
        pair = self.strategy.get_next_pair()
        self.assertEqual(pair, next_pair)

    def test_strategy_get_pairs_traditional(self):
        self.strategy_setUp()
        self.strategy = TraditionalStrategy(self.players_list)
        list_of_pairs = self.strategy.get_pairs()
        pairs = {}
        for pair in list_of_pairs:
            if pair[0] in pairs:
                self.assertEqual(pairs[pair[0]], pair[1])
                self.assertEqual(pairs[pair[1]], pair[0])
            else:
                pairs[pair[0]] = pair[1]
                pairs[pair[1]] = pair[0]

    def test_strategy_is_pair_odd(self):
        self.strategy_setUp()
        self.strategy = OddStrategy(self.players_list)
        self.assertFalse(self.strategy.is_pair_strategy())

    def test_strategy_get_next_pair_odd(self):
        """Test get_next_pair in OddStrategy
        """
        self.strategy_setUp()
        self.strategy = OddStrategy(self.players_list)
        self.strategy_test_run_rounds()

    def test_strategy_get_same_pair_odd(self):
        """Test get_next_pair in TraditionalStrategy when is_same_pair == True
        """
        self.strategy_setUp()
        self.strategy = OddStrategy(self.players_list)
        self.strategy.is_same_pair = True
        pair = self.strategy.get_next_pair()
        ok_pair = (self.players_list[0],
                   self.players_list[1])
        self.assertEqual(pair, ok_pair)

        ok_pair = (self.players_list[1],
                   self.players_list[2])
        pair = self.strategy.get_next_pair()
        self.assertEqual(pair, ok_pair)

        for round_num in range(self.round_cnt):
            ok_pair = self.strategy.get_next_pair()
            self.strategy.is_same_pair = True
            pair = self.strategy.get_next_pair()
            self.assertEqual(pair, ok_pair)

    def test_strategy_del_and_get_next_pair_odd(self):
        self.strategy_setUp()
        self.strategy = OddStrategy(self.players_list)
        first_pair = (self.players_list[0],
                      self.players_list[1])
        self.strategy.is_players_deleted = True
        try:
            pair = self.strategy.get_next_pair()
        except:
            self.fail(msg='cannot get first pair when is_pairs_deleted')
        self.assertEqual(pair, first_pair)

        self.strategy.list_of_players.remove(first_pair[0])
        next_pair = (self.players_list[0],
                     self.players_list[1])
        self.strategy.is_players_deleted = True
        pair = self.strategy.get_next_pair()
        self.assertEqual(pair, next_pair)

    def test_players_init_and_set_strategy(self):
        NUM_FIELDS = 2
        val = range(NUM_FIELDS)

        pls = Players(val[0])
        self.assertEqual(pls.players, val[0])
        self.assertEqual(pls.players_select_strategy, None)

        pls.set_next_pair_strategy(val[1])
        self.assertEqual([pls.players, pls.players_select_strategy], val)

    def test_players_add(self):
        NUM_PLAYERS = 50
        pls = Players([])
        player = [Player(str(i), []) for i in range(0, NUM_PLAYERS)]

        for i in range(NUM_PLAYERS):
            pls.add(player[i])
            self.assertEqual(pls.players, player[:i + 1])

    def test_players_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        pls1 = Players(None)
        pls2 = Players(None)
        self.assertTrue(pls1 == pls2)
        self.assertFalse(pls1 != pls2)

        pls1 = NextPairStrategy(val[0])
        pls2 = NextPairStrategy(val[1])
        self.assertTrue(pls1 != pls2)
        self.assertFalse(pls1 == pls2)

        pls1 = Players(None)
        pls2 = Players(None)
        pls1.set_next_pair_strategy(val[0])
        pls2.set_next_pair_strategy(val[1])
        self.assertTrue(pls1 != pls2)
        self.assertFalse(pls1 == pls2)

    def test_players_repr_and_str(self):
        NUM_FIELDS = 2
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        pls = Players(val[0])
        pls.set_next_pair_strategy(val[1])

        strpls = str(pls)
        self.assertEqual(strpls, pls.__repr__())
        for part in strpls.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Players)')

    def test_hat_init(self):
        NUM_FIELDS = 1
        val = range(NUM_FIELDS)

        hat = Hat(*val)
        self.assertEqual([hat.words], val)

    def test_hat_add_word(self):
        NUM_WORDS = 50
        hat = Hat([])
        word = [Word(str(i*i), str(i)) for i in range(NUM_WORDS)]

        for i in range(NUM_WORDS):
            hat.add_word(word[i])
            self.assertEqual(hat.words, word[:i + 1])

    def test_hat_next_word(self):
        NUM_WORDS = 50
        hat = Hat([])
        word = [str(i) for i in range(NUM_WORDS)]
        words = [Word(Player(str(i*i), []), word[i]) for i in range(NUM_WORDS)]

        for w in words:
            hat.add_word(w)

        for i in range(NUM_WORDS):
            try:
                w = hat.next_word()
            except:
                self.fail(msg='cannot get next word from hat')
            try:
                self.assertTrue(w.text in word)
                word.remove(w.text)
            except:
                self.fail(msg='next word ' + str(w.text) + ' is not in list')

        self.assertEqual(len(word), 0)

    def test_hat_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        hat1 = Hat(None)
        hat2 = Hat(None)
        self.assertTrue(hat1 == hat2)
        self.assertFalse(hat1 != hat2)

        hat1 = Hat(val[0])
        hat2 = Hat(val[1])
        self.assertTrue(hat1 != hat2)
        self.assertFalse(hat1 == hat2)

    def test_hat_repr_and_str(self):
        NUM_FIELDS = 1
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        hat = Hat(*val)

        strhat = str(hat)
        self.assertEqual(strhat, hat.__repr__())
        for part in strhat.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Hat)')

    def test_player_init(self):
        NUM_FIELDS = 2
        val = range(NUM_FIELDS)

        player = Player(*val)
        self.assertEqual([player.name, player.words,
                          player.finished_entering], val + [False])

    def test_player_add_word(self):
        NUM_WORDS = 50
        player = Player('', [])
        word = [Word(str(i*i), str(i)) for i in range(NUM_WORDS)]

        for i in range(NUM_WORDS):
            player.add_word(word[i])
            self.assertEqual(player.words, word[:i + 1])

    def test_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        pl1 = Player(None, None)
        pl2 = Player(None, None)
        self.assertTrue(pl1 == pl2)
        self.assertFalse(pl1 != pl2)

        pl1 = Player(val[0], None)
        pl2 = Player(val[1], None)
        self.assertTrue(pl1 != pl2)
        self.assertFalse(pl1 == pl2)

        pl1 = Player(None, val[0])
        pl2 = Player(None, val[1])
        self.assertTrue(pl1 != pl2)
        self.assertFalse(pl1 == pl2)

        pl1 = Player(None, None)
        pl2 = Player(None, None)
        pl1.finished_entering, pl2.finished_entering = val
        self.assertTrue(pl1 != pl2)
        self.assertFalse(pl1 == pl2)

    def test_player_repr_and_str(self):
        NUM_FIELDS = 3
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        player = Player(val[0], val[1])
        player.finished_entering = val[2]

        strpl = str(player)
        self.assertEqual(strpl, player.__repr__())
        for part in strpl.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Player)')

    def test_word_init(self):
        NUM_FIELDS = 2
        val = range(NUM_FIELDS)

        word = Word(*val)
        self.assertEqual([word.owner, word.text], val)

    def test_word_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        word1 = Word(Player(None, None), None)
        word2 = Word(Player(None, None), None)
        self.assertTrue(word1 == word2)
        self.assertFalse(word1 != word2)

        word1 = Word(Player(val[0], None), None)
        word2 = Word(Player(val[1], None), None)
        self.assertTrue(word1 != word2)
        self.assertFalse(word1 == word2)

        word1 = Word(Player(None, None), val[0])
        word2 = Word(Player(None, None), val[1])
        self.assertTrue(word1 != word2)
        self.assertFalse(word1 == word2)

    def test_word_repr_and_str(self):
        NUM_FIELDS = 2
        val = [Player('0', None), '1']
        was = [False] * NUM_FIELDS

        word = Word(*val)

        strword = str(word)
        self.assertEqual(strword, word.__repr__())
        for part in strword.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Word)')

    def test_settings_init(self):
        NUM_FIELDS = 5
        val = range(NUM_FIELDS)

        settings = Settings(*val)
        self.assertEqual([settings.words_number, settings.time_per_round_sec,
                          settings.max_round_num,
                          settings.time_before_out_sec, settings.skip_words],
                         val)

    def test_settings_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        set1 = Settings(None, None, None, None, None)
        set2 = Settings(None, None, None, None, None)
        self.assertTrue(set1 == set2)
        self.assertFalse(set1 != set2)

        set1 = Settings(val[0], None, None, None, None)
        set2 = Settings(val[1], None, None, None, None)
        self.assertTrue(set1 != set2)
        self.assertFalse(set1 == set2)

        set1 = Settings(None, val[0], None, None, None)
        set2 = Settings(None, val[1], None, None, None)
        self.assertTrue(set1 != set2)
        self.assertFalse(set1 == set2)

        set1 = Settings(None, None, val[0], None, None)
        set2 = Settings(None, None, val[1], None, None)
        self.assertTrue(set1 != set2)
        self.assertFalse(set1 == set2)

        set1 = Settings(None, None, None, val[0], None)
        set2 = Settings(None, None, None, val[1], None)
        self.assertTrue(set1 != set2)
        self.assertFalse(set1 == set2)

        set1 = Settings(None, None, None, None, val[0])
        set2 = Settings(None, None, None, None, val[1])
        self.assertTrue(set1 != set2)
        self.assertFalse(set1 == set2)

    def test_settings_repr_and_str(self):
        NUM_FIELDS = 5
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        settings = Settings(*val)

        strset = str(settings)
        self.assertEqual(strset, settings.__repr__())
        for part in strset.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Settings)')

    def test_word_guess_result_init(self):
        NUM_FIELDS = 3
        val = range(NUM_FIELDS)

        wgs = WordGuessResult(*val)
        self.assertEqual([wgs.word, wgs.result, wgs.time], val)

    def test_word_guess_result_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        wgr1 = WordGuessResult(None, None, None)
        wgr2 = WordGuessResult(None, None, None)
        self.assertTrue(wgr1 == wgr2)
        self.assertFalse(wgr1 != wgr2)

        wgr1 = WordGuessResult(val[0], None, None)
        wgr2 = WordGuessResult(val[1], None, None)
        self.assertTrue(wgr1 != wgr2)
        self.assertFalse(wgr1 == wgr2)

        wgr1 = WordGuessResult(None, val[0], None)
        wgr2 = WordGuessResult(None, val[1], None)
        self.assertTrue(wgr1 != wgr2)
        self.assertFalse(wgr1 == wgr2)

        wgr1 = WordGuessResult(None, None, val[0])
        wgr2 = WordGuessResult(None, None, val[1])
        self.assertTrue(wgr1 != wgr2)
        self.assertFalse(wgr1 == wgr2)

    def test_word_guess_result_repr_and_str(self):
        NUM_FIELDS = 3
        val = range(NUM_FIELDS)
        was = [False] * NUM_FIELDS

        wgr = WordGuessResult(*val)

        strwgr = str(wgr)
        self.assertEqual(strwgr, wgr.__repr__())
        for part in strwgr.split():
            was[int(part)] = True
        for i in range(0, len(was)):
            self.assertTrue(was[i],
                            msg='there is no attribute number ' + str(i) +
                            ' in str(Settings)')

    def test_game_type_init(self):
        NUM_FIELDS = 1
        val = range(NUM_FIELDS)

        gt = GameType(*val)
        self.assertEqual([gt.type], val)
        self.assertRaises(Exception, GameType, GameTypes.NUM_TYPES)

    def test_game_type_eq_and_neq(self):
        val = range(2)
        self.assertNotEqual(*val)

        gt1 = GameType(None)
        gt2 = GameType(None)
        self.assertTrue(gt1 == gt2)
        self.assertFalse(gt1 != gt2)

        gt1 = GameType(val[0])
        gt2 = GameType(val[1])
        self.assertTrue(gt1 != gt2)
        self.assertFalse(gt1 == gt2)

    def test_game_type_repr_and_str(self):
        num_types = GameTypes.NUM_TYPES
        for type in range(num_types):
            gt = GameType(type)
            self.assertEqual(str(gt), gt.__repr__())


if __name__ == "__main__":
    unittest.main()
